iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 21
1

目前我的整個漸進式網站(PWA)是使用「http-server」這個node package來運行的,不過這也僅在開發階段可行而已。之後為了讓其他用戶也能使用,遲早都需要架一個真的server來讓我的網站能對外服務。

但是有沒有更快速的開發方法,讓開發人員能夠不必去煩惱要如何建立和維護伺服器,而只要專注於撰寫server端的程式碼羅就好呢?

事實上,有一種架構叫做Serverless。不是沒有server,而是不用去擔心維護server這件事,不管是在部署還是開發,都是以一個個function為單位,這帶來了程式碼上的高度decoupling。


這裡我要使用的是Firebase提供的Cloud Functions,來實作後端的Restful API。由於Cloud Functions屬於Serverless服務,所以我不需要再花時間建一個實體server來實作我的API,因此這對於開發人員來說是不是太方便了呢 :))

Firebase Cloud Functions

要在我的專案來使用Firebase Cloud Funcitons,首先必須先安裝firebase-tools:

npm install -g firebase-tools

安裝完成後,terminal進到我的PWA project目錄下,執行初始化的動作:

firebase init

接著會跳出一些要求你要進行設定的問題,基本上都是按Yes,記得選Functions和Hosting這兩個:

完成後在專案目錄中會多出幾個檔案和資料夾:

.
+-- functions
|   +-- node_modules
|   +-- index.js
|   +-- package.json
|   +-- package-lock.json
+-- 404.html
+-- firebase.json
+-- .firebaserc

在functions資料夾中的index.js,就是我今天要撰寫Restful API的地方。由於firebase cloud functions是運行在node環境下,所以你也可以在index.js中寫任何的node server-side code(像是express)。

開始來build Restful API吧:/images/emoticon/emoticon07.gif

說明一下我的API主要的功能是什麼,之前我都是直接在前端用firebse API Endpoint去firebase撈貼文資料。現在前端是要去呼叫我自己寫的API來取得Firebase Database的資料。也就是說,我目前的API要有能夠去向firebase取得資料並回傳的功能。

index.js一開始已經有幫我們寫了一個範例程式,那就直接從範例開始改ㄅ:

首先,要先安裝2個module,分別是「firebase-admin」和「cors」。

  • firebase-admin是firebase官方提供的SDK,能夠讓我去存取firebase Database
  • cors則是為了要讓我的API能夠發送和接收跨來源(domain)的請求

在index.js中倒入這兩個modules:

var admin = require('firebase-admin');
var cors = require('cors')({origin: true});

在firebase官方釋出的範例,要使用它的SDK還必須下載並設定你自己的私鑰(這裡我下載完後命名為trip-diary-firebase-key.json),並開始進行初始化:

var serviceAccount = require("./trip-diary-firebase-key.json");
admin.initializeApp({
    credential: admin.credential.cert(serviceAccount),
    databaseURL: "https://trip-diary-f56de.firebaseio.com"
});

接下來取消index.js中的註解後,可以開始撰寫我們的API了:(我把function命名為storePostData)

exports.storePostData = functions.https.onRequest((request, response) => {
    cors(request, response, function() {
        admin.database().ref('posts').push({
            id: request.body.id,
            title: request.body.title,
            location: request.body.location,
            image: request.body.image
        }).then(function() {
            response.status(201).json({message: 'Data Stored', id: request.body.id});
        }).catch(function(err) {
            response.status(500).json({error: err});
        })
    });
});

這個function它會等待2個參數(request和response),我會使用剛剛載好的cors modules提供的cors方法來將我要處理的request、response和要執行的callback fucntion放進去。

在callback function我使用「admin.database().ref('posts').push()」來將收到的request貼文資料添加到Firebase RealTime Database中。

最後一個正常的API總是要回傳結果吧,這裡我會回傳一個json object,包含「一個message」和「儲存到firebase的貼文資料id」。

為何需要id哩?還記得之前在實作背景同步時,當網路恢復service worker將貼文傳送出去後,都會將暫存在indexedDB的資料給刪除,而刪除時需要根據該則貼文的id來進行刪除。

目前我們的API function先寫到這,接著就是要將整個project部署到firebase的主機上(記得我們當初有勾選Hosting這個服務),在terminal中輸入:

firebase deploy

完成後可以看到以下資訊:

我們的API Endpoint網址:https://us-central1-trip-diary-f56de.cloudfunctions.net/storePostData

接著在sw.js還有feed.js中原本直接發出POST Request "https://trip-diary-f56de.firebaseio.com/posts.json" 的地方應該改成上方的 "https://us-central1-trip-diary-f56de.cloudfunctions.net/storePostData" 。

最後在service worker中背景同步的部分,當網路恢復時除了每次傳送貼文資料到我們自己寫的的API之外,也會呼叫deleteItemFromData()來刪除indexedDB中待處理的資料。還記得我們的API會回傳貼文的id吧,所以這部分應該些改成:

... 以上省略 ...
console.log('Send data', res);
if(res.ok) {
    res.json().then(function(resData) {
        deleteItemFromData('sync-posts', resData.id);
    });
}
... 以下省略 ...

Background Sync實作到這邊算是到一個段落惹!! 呼~~/images/emoticon/emoticon06.gif

要測試背景同步成功與否,首先把網路關掉再傳送一次貼文,然後到indexedDB的sync-posts中發現該貼文儲到在裡面。最後當網路恢復時,在console中可以發現背景同步已經成功執行的log就代表成功囉!!

Day21 結束!! /images/emoticon/emoticon07.gif


上一篇
[Day20] 了解PWA中的背景同步(Part2)
下一篇
[Day22] 實作PWA推播通知(Part1)
系列文
你應該要知道的新一代Web技術---漸進式網頁(PWA)29
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言